#include <mcp/error.h>
#include <sstream>
#include <system_error>

namespace mcp {

// Constructors
Error::Error(ErrorCode code, std::string message)
    : code_(code), message_(std::move(message)) {}

Error::Error(ErrorCode code, std::string message, mcp::json::optional<json::Value> data)
    : code_(code), message_(std::move(message)), data_(std::move(data)) {}

// std::exception interface
const char* Error::what() const noexcept {
    if (full_message_.empty()) {
        std::ostringstream oss;
        oss << "[" << static_cast<int>(code_) << "] " << message_;
        if (data_) {
            oss << " (data: " << data_->serialize() << ")";
        }
        full_message_ = oss.str();
    }
    return full_message_.c_str();
}

// Factory methods
Error Error::parse_error(std::string message) {
    return Error(ErrorCode::ParseError, std::move(message));
}

Error Error::invalid_request(std::string message) {
    return Error(ErrorCode::InvalidRequest, std::move(message));
}

Error Error::method_not_found(std::string method) {
    return Error(ErrorCode::MethodNotFound, std::move(method));
}

Error Error::invalid_params(std::string message) {
    return Error(ErrorCode::InvalidParams, std::move(message));
}

Error Error::internal_error(std::string message) {
    return Error(ErrorCode::InternalError, std::move(message));
}

Error Error::unauthorized(std::string message) {
    return Error(ErrorCode::Unauthorized, std::move(message));
}

Error Error::forbidden(std::string message) {
    return Error(ErrorCode::Forbidden, std::move(message));
}

Error Error::not_found(std::string resource) {
    return Error(ErrorCode::NotFound, std::move(resource));
}

Error Error::conflict(std::string message) {
    return Error(ErrorCode::Conflict, std::move(message));
}

Error Error::unsupported_operation(std::string operation) {
    return Error(ErrorCode::UnsupportedOperation, std::move(operation));
}

Error Error::timeout(std::string message) {
    return Error(ErrorCode::RequestTimeout, std::move(message));
}

Error Error::protocol_error(std::string message) {
    return Error(ErrorCode::ProtocolError, std::move(message));
}

// ErrorCategory implementation
const char* ErrorCategory::name() const noexcept {
    return "MCP";
}

std::string ErrorCategory::message(int ev) const {
    ErrorCode code = static_cast<ErrorCode>(ev);
    switch (code) {
        // Standard JSON-RPC errors
        case ErrorCode::ParseError:
            return "Parse error: Invalid JSON was received";
        case ErrorCode::InvalidRequest:
            return "Invalid Request: The JSON sent is not a valid Request object";
        case ErrorCode::MethodNotFound:
            return "Method not found: The method does not exist / is not available";
        case ErrorCode::InvalidParams:
            return "Invalid params: Invalid method parameter(s)";
        case ErrorCode::InternalError:
            return "Internal error: Internal JSON-RPC error";

        // MCP specific errors
        case ErrorCode::Unauthorized:
            return "Unauthorized: Authentication failed";
        case ErrorCode::Forbidden:
            return "Forbidden: Insufficient permissions";
        case ErrorCode::NotFound:
            return "Not Found: Requested resource not found";
        case ErrorCode::Conflict:
            return "Conflict: Resource state conflicts with request";
        case ErrorCode::ResourceConflict:
            return "Resource Conflict: Resource cannot be accessed due to conflicts";
        case ErrorCode::UnsupportedOperation:
            return "Unsupported Operation: Operation is not supported";
        case ErrorCode::RequestTimeout:
            return "Request Timeout: Request timed out";
        case ErrorCode::ProtocolError:
            return "Protocol Error: MCP protocol violation";

        // Validation errors
        case ErrorCode::InvalidUri:
            return "Invalid URI: URI format is invalid";
        case ErrorCode::InvalidSchema:
            return "Invalid Schema: JSON schema validation failed";
        case ErrorCode::ValidationFailed:
            return "Validation Failed: Input validation failed";

        default:
            return "Unknown error";
    }
}

std::error_condition ErrorCategory::default_error_condition(int ev) const noexcept {
    ErrorCode code = static_cast<ErrorCode>(ev);

    // Map to standard error conditions
    switch (code) {
        case ErrorCode::ParseError:
        case ErrorCode::InvalidRequest:
        case ErrorCode::MethodNotFound:
        case ErrorCode::InvalidParams:
            return std::errc::invalid_argument;

        case ErrorCode::InternalError:
        case ErrorCode::ProtocolError:
            return std::errc::state_not_recoverable;

        case ErrorCode::Unauthorized:
        case ErrorCode::Forbidden:
            return std::errc::permission_denied;

        case ErrorCode::NotFound:
            return std::errc::no_such_file_or_directory;

        case ErrorCode::Conflict:
        case ErrorCode::ResourceConflict:
            return std::errc::device_or_resource_busy;

        case ErrorCode::RequestTimeout:
            return std::errc::timed_out;

        case ErrorCode::UnsupportedOperation:
            return std::errc::operation_not_supported;

        case ErrorCode::InvalidUri:
        case ErrorCode::InvalidSchema:
        case ErrorCode::ValidationFailed:
            return std::errc::invalid_argument;

        default:
            return std::errc(0);
    }
}

// Global error category instance
const ErrorCategory& mcp_error_category() noexcept {
    static ErrorCategory instance;
    return instance;
}

// Error code creation functions - already defined inline in header

std::error_condition make_error_condition(ErrorCode e) noexcept {
    return std::error_condition(static_cast<int>(e), mcp_error_category());
}

// Helper functions for error handling
std::string error_code_to_string(ErrorCode code) {
    return mcp_error_category().message(static_cast<int>(code));
}

bool is_json_rpc_error(ErrorCode code) {
    return code == ErrorCode::ParseError ||
           code == ErrorCode::InvalidRequest ||
           code == ErrorCode::MethodNotFound ||
           code == ErrorCode::InvalidParams ||
           code == ErrorCode::InternalError;
}

bool is_mcp_error(ErrorCode code) {
    return !is_json_rpc_error(code) && code != ErrorCode::InvalidUri &&
           code != ErrorCode::InvalidSchema && code != ErrorCode::ValidationFailed;
}

bool is_validation_error(ErrorCode code) {
    return code == ErrorCode::InvalidUri ||
           code == ErrorCode::InvalidSchema ||
           code == ErrorCode::ValidationFailed;
}

// Error severity classification
enum class ErrorSeverity {
    Low,      // User errors, recoverable
    Medium,   // Configuration errors, might need attention
    High,     // Protocol violations, need investigation
    Critical  // System errors, immediate attention required
};

ErrorSeverity get_error_severity(ErrorCode code) {
    switch (code) {
        // Low severity - user input errors
        case ErrorCode::InvalidParams:
        case ErrorCode::ValidationFailed:
        case ErrorCode::InvalidUri:
        case ErrorCode::InvalidSchema:
            return ErrorSeverity::Low;

        // Medium severity - configuration and permission issues
        case ErrorCode::Unauthorized:
        case ErrorCode::Forbidden:
        case ErrorCode::NotFound:
        case ErrorCode::MethodNotFound:
        case ErrorCode::UnsupportedOperation:
            return ErrorSeverity::Medium;

        // High severity - protocol and system issues
        case ErrorCode::ParseError:
        case ErrorCode::InvalidRequest:
        case ErrorCode::Conflict:
        case ErrorCode::ResourceConflict:
        case ErrorCode::RequestTimeout:
        case ErrorCode::ProtocolError:
            return ErrorSeverity::High;

        // Critical severity - internal system failures
        case ErrorCode::InternalError:
            return ErrorSeverity::Critical;

        default:
            return ErrorSeverity::Medium;
    }
}

// Error context information
struct ErrorContext {
    ErrorCode code;
    std::string operation;
    std::string component;
    std::string details;
};

// Global error context (thread-local for thread safety)
thread_local std::vector<ErrorContext> g_error_context_stack;

// Error context management
void push_error_context(ErrorCode code, std::string operation, std::string component, std::string details) {
    g_error_context_stack.push_back(ErrorContext{
        code, std::move(operation), std::move(component), std::move(details)
    });
}

void pop_error_context() {
    if (!g_error_context_stack.empty()) {
        g_error_context_stack.pop_back();
    }
}

mcp::json::optional<ErrorContext> get_current_error_context() {
    if (g_error_context_stack.empty()) {
        return mcp::json::optional<ErrorContext>();
    }
    return g_error_context_stack.back();
}

std::vector<ErrorContext> get_error_context_stack() {
    return g_error_context_stack;
}

void clear_error_context_stack() {
    g_error_context_stack.clear();
}

// Enhanced error with context
Error create_error_with_context(ErrorCode code, std::string message, mcp::json::optional<json::Value> data) {
    if (!g_error_context_stack.empty()) {
        const auto& context = g_error_context_stack.back();

        // Add context information to error data
        std::map<std::string, json::Value> context_map;
        context_map["operation"] = json::Value(context.operation);
        context_map["component"] = json::Value(context.component);

        if (!context.details.empty()) {
            context_map["details"] = json::Value(context.details);
        }

        json::Value context_data = json::Value(context_map);

        if (data) {
            context_data["original_data"] = *data;
        }

        return Error(code, std::move(message), mcp::json::optional<json::Value>(context_data));
    }

    return Error(code, std::move(message), std::move(data));
}

// Error logging helper - using function pointer for C++11 compatibility
typedef void(*LoggerFunction)(const std::string&);

void log_error(const Error& error, LoggerFunction logger) {
    if (!logger) {
        return;
    }

    std::ostringstream oss;
    auto severity = get_error_severity(error.code());

    oss << "[" << error_code_to_string(error.code()) << "] ";

    switch (severity) {
        case ErrorSeverity::Low:
            oss << "[LOW] ";
            break;
        case ErrorSeverity::Medium:
            oss << "[MEDIUM] ";
            break;
        case ErrorSeverity::High:
            oss << "[HIGH] ";
            break;
        case ErrorSeverity::Critical:
            oss << "[CRITICAL] ";
            break;
    }

    oss << error.message();

    if (error.data()) {
        oss << " | Data: " << error.data()->serialize();
    }

    // Add context if available
    if (!g_error_context_stack.empty()) {
        const auto& context = g_error_context_stack.back();
        oss << " | Context: " << context.component << "::" << context.operation;
        if (!context.details.empty()) {
            oss << " (" << context.details << ")";
        }
    }

    logger(oss.str());
}

// Error recovery suggestions
std::string get_error_recovery_suggestion(ErrorCode code) {
    switch (code) {
        case ErrorCode::ParseError:
            return "Check JSON syntax and ensure valid UTF-8 encoding";

        case ErrorCode::InvalidRequest:
            return "Verify request format and required fields";

        case ErrorCode::MethodNotFound:
            return "Check method name and available server capabilities";

        case ErrorCode::InvalidParams:
            return "Validate parameter types and required fields";

        case ErrorCode::Unauthorized:
            return "Check authentication credentials and token validity";

        case ErrorCode::Forbidden:
            return "Verify user permissions and resource access rights";

        case ErrorCode::NotFound:
            return "Check resource identifier and availability";

        case ErrorCode::Conflict:
            return "Resolve resource conflicts or retry with different parameters";

        case ErrorCode::RequestTimeout:
            return "Increase timeout or check network connectivity";

        case ErrorCode::InternalError:
            return "Report the issue to system administrators";

        case ErrorCode::InvalidUri:
            return "Verify URI format and scheme";

        case ErrorCode::InvalidSchema:
            return "Check JSON schema structure and validation rules";

        case ErrorCode::ValidationFailed:
            return "Review input validation rules and constraints";

        default:
            return "Contact support if the issue persists";
    }
}

// Error to JSON conversion
json::Value error_to_json(const Error& error) {
    std::map<std::string, json::Value> result_map;
    result_map["code"] = json::Value(static_cast<int64_t>(error.code()));
    result_map["message"] = json::Value(error.message());

    if (error.data()) {
        result_map["data"] = *error.data();
    }

    return json::Value(result_map);
}

mcp::json::optional<Error> json_to_error(const json::Value& json) {
    if (!json.is_object() || !json.contains("code") || !json.contains("message")) {
        return mcp::json::optional<Error>();
    }

    ErrorCode code = static_cast<ErrorCode>(json["code"].as_int64());
    std::string message = json["message"].as_string();

    mcp::json::optional<json::Value> data;
    if (json.contains("data")) {
        data = mcp::json::optional<json::Value>(json["data"]);
    }

    return mcp::json::optional<Error>(Error(code, std::move(message), std::move(data)));
}

} // namespace mcp